﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using startup.Common.Database;

namespace startup.Common
{
    public class Util
    {
        private static int m_Seq = 0;
        static readonly UInt16[] wCRC16Table1 = {
        0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
        0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
        0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
        0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
        0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
        0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
        0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
        0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
        0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
        0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
        0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
        0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
        0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
        0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
        0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
        0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
        0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
        0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
        0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
        0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
        0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
        0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
        0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
        0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
        0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
        0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
        0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
        0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
        0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
        0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
        0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
        0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040,};


        static readonly UInt16[] wCRC16Table2 = {
        0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7,
        0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, };

        #region 字节数组和16进制字符串转换
        public static string BytesToHex(byte[] Buffer, string Separator = "")
        {
            if (Buffer == null)
            {
                throw new ArgumentNullException("Buffer is null");
            }
            return BytesToHex(Buffer, 0, Buffer.Length, Separator);
        }
        public static string BytesToHex(byte[] Buffer, int Offset, int Count)
        {
            return BytesToHex(Buffer, Offset, Count, "");
        }
        public static string BytesToHex(byte[] Buffer, int Offset, int Count, string Separator)
        {
            if (Buffer == null)
            {
                throw new ArgumentNullException("Buffer is null");
            }

            if (Offset < 0 || Offset >= Buffer.Length)
            {
                throw new ArgumentException("Offset out of range");
            }
            if (Count < 0)
            {
                throw new ArgumentException("Count must be greater than 0");
            }
            if (Separator == null)
            {
                Separator = "";
            }


            int EndPos = Offset + Count - 1;
            if (EndPos > Buffer.Length - 1)
            {
                EndPos = Buffer.Length - 1;
            }
            StringBuilder ret = new StringBuilder();
            for (var i = Offset; i <= EndPos; i++)
            {
                if (Separator != "")
                {
                    ret.Append(Separator);
                }
                ret.Append(Buffer[i].ToString("X2"));
            }
            if (ret.Length > 0 && Separator != "")
            {
                ret.Remove(0, Separator.Length);
            }
            return ret.ToString();
        }

        public static string BytesToHex(List<byte> Buffer, string Separator = "")
        {
            if (Buffer == null)
            {
                return "";
            }
            return BytesToHex(Buffer, 0, Buffer.Count, Separator);
        }
        public static string BytesToHex(List<byte> Buffer, int Offset, int Count)
        {
            return BytesToHex(Buffer, Offset, Count, "");
        }
        public static string BytesToHex(List<byte> Buffer, int Offset, int Count, string Separator)
        {
            if (Buffer == null)
            {
                return "";
            }

            if (Offset < 0 || Offset >= Buffer.Count)
            {
                throw new ArgumentException("Offset out of range");
            }
            if (Count < 0)
            {
                throw new ArgumentException("Count must be greater than 0");
            }
            if (Separator == null)
            {
                Separator = "";
            }


            int EndPos = Offset + Count - 1;
            if (EndPos > Buffer.Count - 1)
            {
                EndPos = Buffer.Count - 1;
            }
            StringBuilder ret = new StringBuilder();
            for (var i = Offset; i <= EndPos; i++)
            {
                if (Separator != "")
                {
                    ret.Append(Separator);
                }
                ret.Append(Buffer[i].ToString("X2"));
            }
            if (ret.Length > 0 && Separator != "")
            {
                ret.Remove(0, Separator.Length);
            }
            return ret.ToString();
        }

        public static byte[] HexToBytes(string Hex, string Separator = "")
        {
            if (Hex == null || Hex == "")
            {
                throw new ArgumentNullException("Hex is null or empty");
            }
            if (!(Separator == null || Separator == "" || Separator == " " || Separator == "-"))
            {
                throw new ArgumentException("Separator only support '-' or ' '");
            }
            //无分割符号
            if (Separator == null || Separator == "")
            {
                if ((Hex.Length % 2) != 0)
                {
                    Hex = "0" + Hex;
                }
                var ret = new Byte[Hex.Length / 2];

                for (var i = 0; i < ret.Length; i++)
                {
                    ret[i] = Convert.ToByte(Hex.Substring(i * 2, 2), 16);
                }
                return ret;
            }
            else
            {
                string[] str = Hex.Split(new string[] { Separator }, StringSplitOptions.RemoveEmptyEntries);
                var ret = new Byte[str.Length];
                for (int i = 0; i < str.Length; i++)
                {
                    ret[i] = Convert.ToByte(str[i], 16);
                }
                return ret;
            }
        }
        public static int ConvertDateTimeInt(DateTime time)
        {
            DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
            return (int)(time - startTime).TotalMilliseconds;
        }
        #endregion

        #region 校验
        public static ushort CalCRC16A(byte[] Data, int StartPos, int Len)
        {
            ushort crc = 0xffff;
            for (int i = StartPos; i < StartPos + Len; i++)
            {
                crc = (ushort)(wCRC16Table1[(byte)(crc ^ Data[i] & 0xff)] ^ (crc >> 8));
            }
            return crc;
        }
        public static ushort CalCRC16A(List<byte> Data, int StartPos, int Len)
        {
            ushort crc = 0xffff;
            for (int i = StartPos; i < StartPos + Len; i++)
            {
                crc = (ushort)(wCRC16Table1[(byte)(crc ^ Data[i] & 0xff)] ^ (crc >> 8));
            }
            return crc;
        }

        public static ushort CalCRC16B(byte[] Data, int StartPos, int Len)
        {
            ushort crc = 0x0000;
            byte da = 0;
            while (Len-- != 0)
            {
                da = (byte)((crc >> 12) & 0xff);
                crc <<= 4;
                crc ^= wCRC16Table2[da ^ (Data[StartPos] / 16)];

                da = (byte)((crc >> 12) & 0xff);
                crc <<= 4;
                crc ^= wCRC16Table2[da ^ (Data[StartPos] & 0x0f)];

                StartPos++;

            }
            return crc;
        }
        public static ushort CalCRC16B(List<byte> Data, int StartPos, int Len)
        {
            ushort crc = 0x0000;
            byte da = 0;
            while (Len-- != 0)
            {
                da = (byte)((crc >> 12) & 0xff);
                crc <<= 4;
                crc ^= wCRC16Table2[da ^ (Data[StartPos] / 16)];

                da = (byte)((crc >> 12) & 0xff);
                crc <<= 4;
                crc ^= wCRC16Table2[da ^ (Data[StartPos] & 0x0f)];

                StartPos++;

            }
            return crc;
        }
        public static int Sum16(byte[] Data, int StartPos, int Len)
        {
            int crc = 0;
            for (int i = StartPos; i < StartPos + Len; i++)
            {
                crc += Data[i];
            }
            return crc & 0xffff;
        }
        #endregion

        #region 大端转换
        public static int BitConvertToUInt32(byte[] Data, int StartPos)
        {
            byte[] v = new byte[] { Data[3], Data[2], Data[1], Data[0] };
            return BitConverter.ToInt32(v, 0);
        }
        #endregion
		#region 内码转换
        public static byte[] Utf8ToGB2312Bytes(string src)
        {
            return CodePageConvertBytes(src, "utf-8", "gb2312");
        }
        public static byte[] Utf8ToGB2312Bytes(byte[] src)
        {
            return CodePageConvertBytes(src, "utf-8", "gb2312");
        }
        public static string Utf8ToGB2312(string src)
        {
            return CodePageConvert(src, "utf-8", "gb2312");
        }
        public static string Utf8ToGB2312(byte[] src)
        {
            return CodePageConvert(src, "utf-8", "gb2312");
        }
        public static string CodePageConvert(string srcString,string srcPage,string dstPage)
        {
            Encoding srcEncoder = Encoding.GetEncoding(srcPage);
            Encoding dstEncoder = Encoding.GetEncoding(dstPage);
            byte[] srcByte = srcEncoder.GetBytes(srcString);
            byte[] dstBytes = Encoding.Convert(srcEncoder, dstEncoder, srcByte);
            return dstEncoder.GetString(dstBytes);
        }
        public static string CodePageConvert(string srcString, int srcPage, int dstPage)
        {
            Encoding srcEncoder = Encoding.GetEncoding(srcPage);
            Encoding dstEncoder = Encoding.GetEncoding(dstPage);
            byte[] srcByte = srcEncoder.GetBytes(srcString);
            byte[] dstBytes = Encoding.Convert(srcEncoder, dstEncoder, srcByte);
            return dstEncoder.GetString(dstBytes);
        }
        public static string CodePageConvert(byte[] src,string srcPage, string dstPage)
        {
            Encoding srcEncoder = Encoding.GetEncoding(srcPage);
            Encoding dstEncoder = Encoding.GetEncoding(dstPage);
            byte[] dstBytes = Encoding.Convert(srcEncoder, dstEncoder, src);
            return dstEncoder.GetString(dstBytes);
        }
        public static string CodePageConvert(byte[] src, int srcPage, int dstPage)
        {
            Encoding srcEncoder = Encoding.GetEncoding(srcPage);
            Encoding dstEncoder = Encoding.GetEncoding(dstPage);
            byte[] dstBytes = Encoding.Convert(srcEncoder, dstEncoder, src);
            return dstEncoder.GetString(dstBytes);
        }
        public static byte[] CodePageConvertBytes(string srcString, string srcPage, string dstPage)
        {
            Encoding srcEncoder = Encoding.GetEncoding(srcPage);
            Encoding dstEncoder = Encoding.GetEncoding(dstPage);
            byte[] srcByte = srcEncoder.GetBytes(srcString);
            return Encoding.Convert(srcEncoder, dstEncoder, srcByte);
        }
        public static byte[] CodePageConvertBytes(string srcString, int srcPage, int dstPage)
        {
            Encoding srcEncoder = Encoding.GetEncoding(srcPage);
            Encoding dstEncoder = Encoding.GetEncoding(dstPage);
            byte[] srcByte = srcEncoder.GetBytes(srcString);
            return Encoding.Convert(srcEncoder, dstEncoder, srcByte);
        }
        public static byte[] CodePageConvertBytes(byte[] src, string srcPage, string dstPage)
        {

            Encoding srcEncoder = Encoding.GetEncoding(srcPage);
            Encoding dstEncoder = Encoding.GetEncoding(dstPage);
            return Encoding.Convert(srcEncoder,dstEncoder, src);
        }
        public static byte[] CodePageConvertBytes(byte[] src,int srcPage, int dstPage)
        {
            Encoding srcEncoder = Encoding.GetEncoding(srcPage);
            Encoding dstEncoder = Encoding.GetEncoding(dstPage);
            return Encoding.Convert(srcEncoder, dstEncoder, src);
        }
        #endregion
        #region 数据导出

        public static DbResult<string> Export2CSV(string title, string[] columnTitles, List<Dictionary<string, Object>> columnValues)
        {
            DbResult<string> Result = new DbResult<string>();
            Result.state = 0;
            Result.errMsg = "";

            if ((columnTitles == null) || (columnTitles.Length == 0) || (columnValues == null))
            {
                Result.errMsg = "参数不正确，表头或数据个数为零";
                Result.state = (int)DbFailType.ColumnTitlesNotMatchColumnValues;
                return Result;
            }
            try
            {
                string Path = Directory.GetCurrentDirectory() + @"\wwwroot\ExportTemp\";
                if (!Directory.Exists(Path))
                {
                    Directory.CreateDirectory(Path);
                }
                string FileName = Guid.NewGuid().ToString("N") + ".csv";
                Result.data = @"\ExportTemp\" + FileName;
                FileStream fs = System.IO.File.OpenWrite(Path + FileName);
                fs.Seek(0, SeekOrigin.Begin);
                fs.SetLength(0);
                byte[] titleBytes = Util.Utf8ToGB2312Bytes(title+"\r\n"); //Encoding.UTF8.GetBytes(title);
                // titleBytes= Encoding.GetEncoding("gb2312").GetString(titleBytes)
                fs.Write(titleBytes, 0, titleBytes.Length);
                string columnTitleString = "";
                for (int i = 0; i < columnTitles.Length; i++)
                {
                    columnTitleString += columnTitles[i] + ",";
                }
                titleBytes = Util.Utf8ToGB2312Bytes(columnTitleString + "\r\n");//Encoding.UTF8.GetBytes(columnTitleString + "\r\n");

                fs.Write(titleBytes, 0, titleBytes.Length);
                for (int i = 0; i < columnValues.Count; i++)
                {
                    Dictionary<string, object> row = columnValues[i];
                    string valueString = "";
                    if (row.Count < columnTitles.Length)
                    {
                        Result.errMsg += "第" + i + "行数据定义错误，长度与标题不匹配!";
                        Result.state = (int)DbFailType.ColumnTitlesNotMatchColumnValues;
                        continue;
                    }
                    for (int j = 0; j < columnTitles.Length; j++)
                    {
                        valueString += row["field" + j].ToString() + ",";
                    }
                    byte[] rowValue = Util.Utf8ToGB2312Bytes(valueString + "\r\n");//Encoding.UTF8.GetBytes(valueString + "\r\n");
                    fs.Write(rowValue, 0, rowValue.Length);
                }
                fs.Flush();
                fs.Dispose();
            }
            catch (Exception ex)
            {
                Result.state = -1;
                Result.errMsg = ex.Message;
            }
            return Result;
        }
        #endregion

        public static int GetSeq()
        {
            if (m_Seq > 10000)
            {
                m_Seq = 1;
            }
            else {
                m_Seq++;
            }
            return m_Seq;
        }
        #region 加密解密相关
        #region HMacSha1
        public static string HMacSha1(string input, string key)
        {
            return HMacSha1(Encoding.UTF8.GetBytes(input), Encoding.UTF8.GetBytes(key));
        }
        public static string HMacSha1(byte[] input, byte[] key)
        {
            return HMacSha1(input, 0, input == null ? 0 : input.Length, key);
        }
        public static string HMacSha1(byte[] input, int offset, int len, byte[] key)
        {
            byte[] hashedBytes = HMacSha1Bytes(input, offset, len, key);
            return Convert.ToBase64String(hashedBytes);

        }
        public static string HMacSha1Hex(string input,string key)
        {
            return BytesToHex(HMacSha1Bytes(input, key));
        }
        public static byte[] HMacSha1Bytes(string input,string key)
        {
            return HMacSha1Bytes(Encoding.UTF8.GetBytes(input), Encoding.UTF8.GetBytes(key));
        }
        public static byte[] HMacSha1Bytes(byte[] input, byte[] key)
        {
            return HMacSha1Bytes(input, 0, input == null ? 0 : input.Length, key);
        }
        public static byte[] HMacSha1Bytes(byte[] input, int offset, int len, byte[] key)
        {
            HMACSHA1 sha1 = new HMACSHA1();
            sha1.Key = key;
            return sha1.ComputeHash(input, offset, len);
        }
        #endregion
        #region AES CBC
        public static string AESCBCCipher(string Data, byte[] Key, byte[] iv)
        {
            Byte[] plainBytes = Encoding.UTF8.GetBytes(Data);

            

            Byte[] Cryptograph = null; // 加密后的密文  

            RijndaelManaged Aes = new RijndaelManaged();
            Aes.KeySize = 128;
            Aes.Key = Key;
            Aes.IV = iv;
            Aes.Mode = CipherMode.CBC;
            Aes.Padding = PaddingMode.PKCS7;
            try
            {
                // 开辟一块内存流  
                using (MemoryStream Memory = new MemoryStream())
                {
                    // 把内存流对象包装成加密流对象  
                    using (CryptoStream Encryptor = new CryptoStream(Memory,
                     Aes.CreateEncryptor(Key, iv),
                     CryptoStreamMode.Write))
                    {
                        // 明文数据写入加密流  
                        Encryptor.Write(plainBytes, 0, plainBytes.Length);
                        Encryptor.FlushFinalBlock();

                        Cryptograph = Memory.ToArray();
                    }
                }
            }
            catch
            {
                Cryptograph = null;
            }

            return Convert.ToBase64String(Cryptograph);
        }
        public static string AESCBCDecipher(string Data,byte[] Key,byte[] iv)
        {
            Byte[] encryptedBytes = Convert.FromBase64String(Data);
           

            Byte[] original = null; // 解密后的明文  

            RijndaelManaged Aes = new RijndaelManaged();
            Aes.Padding = PaddingMode.PKCS7;
            Aes.Mode = CipherMode.CBC;
            Aes.KeySize = 128;
            Aes.Key = Key;
            Aes.IV = iv;
            try
            {
                // 开辟一块内存流，存储密文  
                using (MemoryStream Memory = new MemoryStream(encryptedBytes))
                {
                    // 把内存流对象包装成加密流对象  
                    using (CryptoStream Decryptor = new CryptoStream(Memory,
                    Aes.CreateDecryptor(Key, iv),
                    CryptoStreamMode.Read))
                    {
                        // 明文存储区  
                        using (MemoryStream originalMemory = new MemoryStream())
                        {
                            Byte[] Buffer = new Byte[1024];
                            Int32 readBytes = 0;
                            while ((readBytes = Decryptor.Read(Buffer, 0, Buffer.Length)) > 0)
                            {
                                originalMemory.Write(Buffer, 0, readBytes);
                            }

                            original = originalMemory.ToArray();
                        }
                    }
                }
            }
            catch
            {
                original = null;
            }
            return Encoding.UTF8.GetString(original);
        }
        #endregion
        #region MD5
        public static byte[] MD5(string message)
        {
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] output = md5.ComputeHash(Encoding.UTF8.GetBytes( message));
            return output;
        }
		 public static string MD5HexStr(string message)
        {
            return BytesToHex(MD5(message));
        }

        #endregion
        #region HMacMD5
        public static string HMacMD5(string input,string key)
        {
            HMACMD5 md5 = new HMACMD5(Encoding.UTF8.GetBytes(key));
           byte[] outPut=  md5.ComputeHash(Encoding.UTF8.GetBytes(input));
            return BytesToHex(outPut);
        }
        #endregion

        #endregion
    }
}
